package da.metricscraft.client.frames;

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.io.*;
import java.util.ArrayList;

import javax.imageio.ImageIO;
import javax.swing.JPanel;

import da.metricscraft.client.ArrayList2D;
import da.metricscraft.client.frames.layers.*;
import da.metricscraft.client.threads.BiomsXMLLoader;
import da.metricscraft.client.threads.SpatialXMLLoader.MoveActions;

public class GraphicsPanel extends JPanel implements MouseMotionListener, MouseListener, MouseWheelListener{
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private static GraphicsPanel currentPanel;
	private Point prevMousePosition;
	private Point deltaMousePosition;
	private Robot robot;
	private Cursor blankCursor;
	public static final int[] PANELSIZE = {870,660};
	public static final int[] BLOCKSIZE = {1,1};
	private int[] imageSize = {0,0}; 
	public int[] getImageSize() {
		return imageSize;
	}
	public Point getLowest() {
		return lowest;
	}
	public Point getHighest() {
		return highest;
	}
	private Point lowest = new Point();
	private Point highest = new Point();
	private static float scaleFactor = 0.8f;
	private Point canvasPosition = new Point(0,0);
	private ArrayList2D<String, String> list;
	private ArrayList2D<String, String> biomesList;
	private BufferedImage bimg;
	private BufferedImage biomesLayerImg;
	private ArrayList<Layer<?>> layers = new ArrayList<Layer<?>>();
	private boolean isActive = true;
	public static float CurrentScale(){
		return scaleFactor;
	}
	public enum BiomeType{
	    RAINFOREST(Color.green),
	    SWAMPLAND(Color.gray),
	    SEASONAL_FOREST(Color.orange),
	    FOREST(Color.green),
	    SAVANNA(Color.yellow),
	    SHRUBLAND(Color.yellow),
	    TAIGA(Color.cyan),
	    DESERT(Color.yellow),
	    PLAINS(Color.green),
	    ICE_DESERT(Color.white),
	    TUNDRA(Color.cyan),
	    HELL(Color.red),
	    SKY(Color.blue),
	    OCEAN(Color.blue),
	    RIVER(Color.blue),
	    EXTREME_HILLS(Color.red),
	    FROZEN_OCEAN(Color.cyan),
	    FROZEN_RIVER(Color.cyan),
	    ICE_PLAINS(Color.white),
	    ICE_MOUNTAINS(Color.white),
	    MUSHROOM_ISLAND(Color.MAGENTA),
	    MUSHROOM_SHORE(Color.MAGENTA),
	    BEACH(Color.yellow),
	    DESERT_HILLS(Color.yellow),
	    FOREST_HILLS(Color.green),
	    TAIGA_HILLS(Color.cyan),
	    SMALL_MOUNTAINS(Color.red);
	    public Color bColor;
	    public Color getColor(){
	    	return new Color(bColor.getRed(), bColor.getGreen(), bColor.getBlue(), 150);
	    }
	    private BiomeType(Color c){
	    	bColor = c;
	    }
	}
	public enum BlockType{
		STONE(Color.gray),
		GRASS(Color.green),
		DIRT(new Color(0x996600)),
		WOOD(new Color(0x663300)),
		STATIONARY_WATER(Color.blue),
		WATER(Color.blue),
		LAVA(Color.red),
		STATIONARY_LAVA(Color.red),
		SAND(Color.yellow),
		GRAVEL(new Color(0x996666)),
		IRON_ORE(new Color(0xcccc99)),
		COAL_ORE(new Color(0x000033)),
		LEAVES(new Color(0x336600)),
		SANDSTONE(new Color(0xcccc33)),
		SNOW_BLOCK(Color.white);
		BlockType(Color c){
			this.color = c;
		}
		public final Color color;
	}
	public static GraphicsPanel getInstance(){
		if (currentPanel == null){
			currentPanel = new GraphicsPanel();
		}
		return currentPanel;
	}
	public GraphicsPanel(){
		super();
		GraphicsPanel.currentPanel = this;
		//setBackground(Color.black);
		setPreferredSize(new Dimension(PANELSIZE[0],PANELSIZE[1]));
		addMouseMotionListener(this);
		addMouseListener(this);
		addMouseWheelListener(this);
		deltaMousePosition = new Point(0,0);
		try {
			robot = new Robot();
		} catch (AWTException e) {
			e.printStackTrace();
		}
		blankCursor = 
				Toolkit.getDefaultToolkit().createCustomCursor(
						new BufferedImage(16, 16, BufferedImage.TYPE_INT_ARGB), new Point(0, 0), "blank cursor");
		setEnabled(false);
	}
	public void reloadList(BiomsXMLLoader list, Point lowest, Point highest){
		this.list = list.getData();
		biomesList = list.getBiomesMap();
		canvasPosition = new Point(0,0);
		paintImage(lowest, highest);
		
		repaint();
	}
	public void repaintImage(){
		System.out.println("Painting image");
		long heapSize = Runtime.getRuntime().totalMemory()/1024;
		long heapMaxSize = Runtime.getRuntime().maxMemory()/1024;
		long heapFreeSize = Runtime.getRuntime().freeMemory()/1024;
		
		System.out.println("Updating bioms - size: " + heapSize/1024 + " | max: " + heapMaxSize/1024 + " | free: " + heapFreeSize/1024);
		bimg = new BufferedImage(imageSize[0] * BLOCKSIZE[0], imageSize[1] * BLOCKSIZE[1], BufferedImage.TYPE_INT_RGB);
		biomesLayerImg = new BufferedImage(imageSize[0] * BLOCKSIZE[0], imageSize[1] * BLOCKSIZE[1], BufferedImage.TYPE_INT_ARGB);
		Graphics g = bimg.createGraphics();
		Graphics g2 = biomesLayerImg.createGraphics();
		for (int i = 0; i < imageSize[0]; i++){
			for (int j = 0; j < imageSize[1]; j++){
				String type = list.get((i + lowest.x) + "", (j + lowest.y) + "");
				String biome = biomesList.get((i + lowest.x) + "", (j + lowest.y) + "");
				if (biome == null){
					g2.setColor(Color.black);
				}else{
					for(BiomeType b : BiomeType.values()){
						if (biome.equalsIgnoreCase(b.name())){
							g2.setColor(b.getColor());
						}
					}
				}
				g2.fillRect(i,j,1,1);
				if (type == null){
					g.setColor(Color.black);
				}else{
					for(BlockType b : BlockType.values()){
						if (type.equalsIgnoreCase(b.name())){
							g.setColor(b.color);
						}
					}
				}
				g.fillRect(i * BLOCKSIZE[0], j * BLOCKSIZE[1], BLOCKSIZE[0], BLOCKSIZE[1]);
			}
		}
		
		
		try {
			File fi = new File("./test.jpg");
			ImageIO.write(bimg,"PNG", fi);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
		moveFrame((int)(imageSize[0]/2f),(int)(imageSize[1]/2f));
	}
	public void setActive(boolean isActive){
		this.isActive = isActive;
		repaint();
	}
	private void paintImage(Point lowest, Point highest){
		int maxX = highest.x - lowest.x;
		int maxY = highest.y - lowest.y;
		imageSize[0] = maxX; imageSize[1] = maxY;
		this.lowest = lowest;
		this.highest = highest;
		repaintImage();
		robot.mouseWheel(-1);
		robot.mouseWheel(-1);
	}
	public void loadLayers(){
		layers = new ArrayList<Layer<?>>();
		layers.add(new MovementLayer("movement", getGraphics(), new Point(bimg.getWidth(), bimg.getHeight())));

		for (MoveActions action : MoveActions.values()){
			if (action == MoveActions.NONE || action == MoveActions.MOVE) continue;
			layers.add(new SpatialPlayerLayer(action.name(), getGraphics(), new Point(bimg.getWidth(), bimg.getHeight()), action));
		}
	}
	public void activateLayer(String name){
		for (Layer<?> l : layers){
			if (l.getName().equalsIgnoreCase(name)){
				l.setActive(true);
			}
		}
	}
	public void moveFrame(int x, int y){
		canvasPosition.x += x;
		canvasPosition.y += y;
		canvasPosition.x = Math.max(0, Math.min(canvasPosition.x, (int)(bimg.getWidth() - PANELSIZE[0]*scaleFactor)));
		canvasPosition.y = Math.max(0, Math.min(canvasPosition.y, (int)(bimg.getHeight() - PANELSIZE[1]*scaleFactor)));
		repaint();
		for(Layer<?> l : layers)
			l.setCanvasPosition(canvasPosition);
	}
	@Override
	public void repaint() {
		if (bimg != null && getGraphics() != null){

			if (isActive){
				this.getGraphics().drawImage(bimg, 0, 0, PANELSIZE[0], PANELSIZE[1],canvasPosition.x,canvasPosition.y,
						(int)((canvasPosition.x + PANELSIZE[0]*scaleFactor)), (int)((canvasPosition.y + PANELSIZE[1]*scaleFactor)), this.getParent());
				this.getGraphics().drawImage(biomesLayerImg, 0, 0, PANELSIZE[0], PANELSIZE[1],canvasPosition.x,canvasPosition.y,
						(int)((canvasPosition.x + PANELSIZE[0]*scaleFactor)), (int)((canvasPosition.y + PANELSIZE[1]*scaleFactor)), this.getParent());
			}else{
				this.getGraphics().clearRect(0, 0, PANELSIZE[0], PANELSIZE[1]);
				this.getGraphics().drawImage(bimg, 0, 0, PANELSIZE[0], PANELSIZE[1],canvasPosition.x,canvasPosition.y,
						(int)((canvasPosition.x + PANELSIZE[0]*scaleFactor)), (int)((canvasPosition.y + PANELSIZE[1]*scaleFactor)), this.getParent());
			}
		}
		if (layers != null)
			for(Layer<?> l : layers)
				l.draw();
		
	}
	@Override
	public void mouseDragged(MouseEvent e) {
		// TODO Auto-generated method stub
		if (prevMousePosition == null) prevMousePosition = new Point(e.getXOnScreen(), e.getYOnScreen());
		if (e.getXOnScreen() != prevMousePosition.x && e.getYOnScreen() != prevMousePosition.y){
			deltaMousePosition.x = e.getXOnScreen() - prevMousePosition.x;
			deltaMousePosition.y = e.getYOnScreen() - prevMousePosition.y;
			moveFrame(deltaMousePosition.x, deltaMousePosition.y);
			robot.mouseMove(prevMousePosition.x, prevMousePosition.y);
		}
		
	}
	@Override
	public void mouseMoved(MouseEvent e) {
		// TODO Auto-generated method stub
		
	}
	@Override
	public void mouseClicked(MouseEvent e) {
	}
	@Override
	public void mousePressed(MouseEvent e) {
		if (prevMousePosition == null){
			prevMousePosition = new Point(e.getXOnScreen(), e.getYOnScreen());

			this.getParent().setCursor(blankCursor);
		}else{
			prevMousePosition.x = e.getXOnScreen();
			prevMousePosition.y = e.getYOnScreen();
		}
	}
	@Override
	public void mouseReleased(MouseEvent e) {
		this.getParent().setCursor(Cursor.getDefaultCursor());
	}
	@Override
	public void mouseEntered(MouseEvent e) {
		// TODO Auto-generated method stub
		
	}
	@Override
	public void mouseExited(MouseEvent e) {
		// TODO Auto-generated method stub
		
	}
	@Override
	public void mouseWheelMoved(MouseWheelEvent e) {
		if (bimg != null){
			float futureScaleFactor = scaleFactor;
			if (e.getWheelRotation() > 0){
				if (scaleFactor <= 0.7f){
					futureScaleFactor += 0.1f;
				}
			}else if (e.getWheelRotation() < 0){
				if (scaleFactor > 0.1f){
					futureScaleFactor -= 0.1f;
				}
			}
			int mult = -e.getWheelRotation()/Math.abs(e.getWheelRotation());
			if (Math.abs(scaleFactor - futureScaleFactor) <= 0.05) return;
			scaleFactor = futureScaleFactor;
			//moveFrame(0,0);
			moveFrame((int)(PANELSIZE[0]*0.1f * mult/2f),(int)(PANELSIZE[1]*0.1f * mult/2f));
			//moveFrame((int)((PANELSIZE[0]/2f)*scaleFactor + (e.getX() - PANELSIZE[0]/2)*scaleFactor)*mult,(int)((PANELSIZE[1]/2f)*scaleFactor + (e.getY() - PANELSIZE[0]/2)*scaleFactor)*mult);
			
		}
	}
}
